package com.hengyu.data.validator.factory;

import com.hengyu.data.validator.User;
import com.hengyu.data.validator.annotation.Validation;
import com.hengyu.data.validator.bean.Valid;
import com.hengyu.data.validator.exception.ValidatorException;
import com.hengyu.data.validator.factory.validators.Validator;

import java.io.File;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User：恒宇少年
 * Date：2017/3/19
 * Time：21:35
 * 码云：http://git.oschina.net/jnyqy
 * ========================
 */
public class HengYuValidator
{
    //注解的前缀名，如果前缀名称是该包下，则证明可以使用validate()方法
    static final String PREFFIX = "com.hengyu.data.validator.annotation";

    //错误消息列表读取后存放内存
    static final Hashtable<String,String> ERROR_MSGS = new Hashtable<String, String>();

    //错误消息占位符集合,key=>注解名称,value=>占位符数组如：[0] = {min},[1] = {max}
    static final Hashtable<String,String[]> ERROR_MSG_PLACEHOLDER = new Hashtable<String, String[]>();

    //错误消息文件
    static final String ERROR_MSG_zh_CN = "/message_zh_CN.properties";

    /**
     * 执行验证
     * @param valid 实现了Valid接口的实体类
     */
    public synchronized static void doValid(Valid valid) throws Exception
    {
        //获取被验证实体类型
        Class<?> clazz = valid.getClass();
        //判断是否存在@Validation注解
        Validation validation = clazz.getAnnotation(Validation.class);
        //存在
        if(validation != null)
        {
            //-> 获取所有字段
            Field[] fields = clazz.getDeclaredFields();
            //-->遍历字段验证所有字段注解配置是否通过
            for (Field field :
                    fields) {
                validateField(field,valid);
            }
        }
    }

    /**
     * 验证单个字段有效性
     * @param field
     * @throws Exception
     */
    static void validateField(Field field,Valid valid) throws Exception
    {
        //获取字段的所有注解
        Annotation[] annotations = field.getAnnotations();
        //遍历注解列表
        for (Annotation annotation :
                annotations) {
            //获取当前遍历到的注解全路径
            String preffix = annotation.annotationType().getName();
            //截取最后一位保留前包路径
            preffix = preffix.substring(0,preffix.lastIndexOf("."));
            //如果包名相同，执行validate方法
            if(PREFFIX.equals(preffix))
            {
                //根据注解名称获取验证者
                Validator validator = HengYuValidationDynamicFactory.getValidator(annotation);
                //执行验证
                boolean flag = validator.validate(field,valid);
                //验证不通过抛出ValidatorException异常
                if(!flag)
                {
                    throw new ValidatorException(formatterErrorMsg(field,annotation));
                }
            }
        }
    }

    /**
     * 获取该注解对应的错误消息
     * @param annotation
     * @return
     */
    static String formatterErrorMsg(Field field,Annotation annotation) throws Exception
    {
        //如果内存中不存在错误消息
        if(ERROR_MSGS.size() == 0)
        {
            //获取错误消息文件输入流
            InputStream inputStream = HengYuValidator.class.getResourceAsStream(ERROR_MSG_zh_CN);
            //读取文件中的key->value键值对
            Properties properties = new Properties();
            properties.load(inputStream);
            Set<Object> set = properties.keySet();
            Iterator<Object> iterator = set.iterator();
            //遍历properties文件内容存放到内存
            while(iterator.hasNext())
            {
                //获取key
                String key = iterator.next().toString();
                //获取value
                String value = properties.getProperty(key);
                //存放到内存
                ERROR_MSGS.put("{"+key+"}",value);
            }
        }
        //反射获取注解message方法
        Method messageMethod = annotation.annotationType().getDeclaredMethod("message");
        //执行方法获取对应properties文件内的messageId
        String messageId = messageMethod.invoke(annotation).toString();
        //获取未格式化的错误消息
        String notFormatter = ERROR_MSGS.get(messageId);
        //返回格式化后的错误信息
        return formattedErrorMsg(field,annotation,notFormatter);
    }

    /**
     * 获取未被格式化的占位符
     * @param annotation 注解
     * @param notFormatter 未被格式化的错误字符串
     * @return key=>占位符名称如{min}，value：annotation内具体配置的值
     */
    static String formattedErrorMsg(Field field,Annotation annotation,String notFormatter) throws Exception
    {
        //获取注解名称
        String annotationName = annotation.annotationType().getSimpleName();
        //先从内存中获取占位符
        String[] placeholders = ERROR_MSG_PLACEHOLDER.get(annotationName);
        if(placeholders == null) {
            //分割字符串为char数组，一个字符一个占位
            char[] charArray = notFormatter.toCharArray();
            //临时字符串用来保存{xxx}这种格式内容多个使用","隔开
            String temp = "";
            //标识是否可以开始记录字符到temp字符串内
            boolean isStart = false;
            //遍历char数组
            for (int i = 0; i < charArray.length; i++) {
                //当前遍历到的char数组的单个字符
                String current = charArray[i] + "";
                //如果当前字符是"{",表示可以开始记录字符到temp，打开isStart标识
                if (current.equals("{")) {
                    isStart = true;
                }
                //如果可以记录，追加temp字符串
                if (isStart) {
                    temp += current;
                }
                //如果当前字符是"}"，表示可以结束记录,关闭isStart标识
                if (current.equals("}")) {
                    isStart = false;
                    //每个后面添加","用于分割使用
                    temp += ",";
                }
            }
            //如果存在占位符
            if (temp.length() > 0) {
                //获取占位符数组
                String[] placeholderArray = temp.split(",");
                //替换临时变量为最新获取数组
                placeholders = placeholderArray;
                //添加到内存中
                ERROR_MSG_PLACEHOLDER.put(annotationName,placeholderArray);
            }
        }
        //如果存在占位符，遍历替换占位符为注解配置对应值
        if(placeholders != null) {
            //遍历占位符替换对应的值
            for (String placeholder :
                    placeholders) {
                String methodName = placeholder.replace("{", "").replace("}", "");
                Method method = annotation.annotationType().getDeclaredMethod(methodName);
                String placeholdeVal = method.invoke(annotation).toString();
                //替换占位符为注解配置的对应值
                notFormatter = notFormatter.replace(placeholder, placeholdeVal);
            }
        }
        //获取注解被配置的字段名称追加到错误消息前
        notFormatter = field.getName() + notFormatter;
        return notFormatter;
    }

    public static void main(String[] args) throws Exception
    {
        /*long start = System.currentTimeMillis();
        try {

            User user = new User();
            user.setName("");
            doValid(user);
        }catch (Exception e)
        {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("毫秒：" + (end - start));*/
        System.out.println(Hashtable.class.getName());
    }
}
